#!/bin/bash
# MySQL连接超时（网络问题）自动处理脚本
FIX_CODE_ID="mysql_connection"

# 配置参数 - 根据实际环境修改
MYSQL_HOST="localhost"             # MySQL主机地址（可能是远程服务器）
MYSQL_PORT=3306                    # MySQL端口
MYSQL_USER="root"                  # MySQL测试用户
MYSQL_PASSWORD="your_mysql_password"# MySQL测试密码
CHECK_TIMEOUT=10                   # 连接检查超时时间(秒)
RETRY_ATTEMPTS=3                   # 重试次数
NETWORK_CHECK_HOSTS=("8.8.8.8" "114.114.114.114")  # 网络连通性测试地址
FIREWALL_SERVICE="firewalld"       # 防火墙服务名（firewalld/ufw）
LOG_FILE="/var/log/mysql_connection.log"
MYSQL_SERVICE="mysql"              # MySQL服务名
CONN_TIMEOUT_PARAM="wait_timeout"  # 连接超时参数名
CONN_TIMEOUT_VALUE=300             # 连接超时参数值(秒)

# 日志函数
log() {
    echo "[$(date +'%Y-%m-%d %H:%M:%S')] $1" >> "$LOG_FILE"
}

# 测试MySQL连接
test_mysql_connection() {
    log "测试MySQL连接: $MYSQL_HOST:$MYSQL_PORT"
    
    # 使用mysqladmin测试连接
    if mysqladmin -h"$MYSQL_HOST" -P"$MYSQL_PORT" -u"$MYSQL_USER" -p"$MYSQL_PASSWORD" ping -i 1 -c 3 -W "$CHECK_TIMEOUT" >/dev/null 2>&1; then
        log "MySQL连接测试成功"
        return 0
    else
        log "MySQL连接测试失败（超时或拒绝连接）"
        return 1
    fi
}

# 检查网络基础连通性
check_network_connectivity() {
    log "检查网络基础连通性"
    
    # 测试DNS解析
    if ! nslookup "$MYSQL_HOST" >/dev/null 2>&1; then
        log "DNS解析$MYSQL_HOST失败"
        return 1
    fi
    
    # 测试到MySQL主机的网络连通性
    if ! ping -c 3 -W 5 "$MYSQL_HOST" >/dev/null 2>&1; then
        log "无法ping通$MYSQL_HOST"
        return 1
    fi
    
    # 测试到公共DNS的连通性
    for host in "${NETWORK_CHECK_HOSTS[@]}"; do
        if ping -c 2 -W 3 "$host" >/dev/null 2>&1; then
            log "网络基础连通性正常（通过$host验证）"
            return 0
        fi
    done
    
    log "网络基础连通性异常"
    return 1
}

# 检查端口是否可达
check_port_accessibility() {
    log "检查端口$MYSQL_HOST:$MYSQL_PORT可达性"
    
    # 使用nc命令检查端口
    if nc -z -w "$CHECK_TIMEOUT" "$MYSQL_HOST" "$MYSQL_PORT"; then
        log "端口$MYSQL_PORT可达"
        return 0
    else
        log "端口$MYSQL_PORT不可达"
        return 1
    fi
}

# 检查并修复防火墙规则
fix_firewall_rules() {
    log "检查并修复防火墙规则"
    
    if [ "$FIREWALL_SERVICE" = "firewalld" ]; then
        # 检查firewalld规则
        if firewall-cmd --list-ports | grep -q "$MYSQL_PORT/tcp"; then
            log "防火墙已允许$MYSQL_PORT/tcp端口"
            return 0
        else
            log "添加$MYSQL_PORT/tcp到防火墙允许列表"
            firewall-cmd --add-port="$MYSQL_PORT/tcp" --permanent
            firewall-cmd --reload
            return $?
        fi
    elif [ "$FIREWALL_SERVICE" = "ufw" ]; then
        # 检查ufw规则
        if ufw status | grep -q "ALLOW.*$MYSQL_PORT/tcp"; then
            log "防火墙已允许$MYSQL_PORT/tcp端口"
            return 0
        else
            log "添加$MYSQL_PORT/tcp到防火墙允许列表"
            ufw allow "$MYSQL_PORT/tcp"
            return $?
        fi
    else
        log "不支持的防火墙服务: $FIREWALL_SERVICE"
        return 1
    fi
}

# 重启网络服务
restart_network_service() {
    log "尝试重启网络服务"
    
    if command -v systemctl &> /dev/null; then
        if systemctl restart network; then
            log "网络服务重启成功"
            sleep 10  # 等待网络恢复
            return 0
        else
            log "网络服务重启失败"
            return 1
        fi
    else
        log "不支持的系统服务管理器"
        return 1
    fi
}

# 重启MySQL服务
restart_mysql_service() {
    log "尝试重启MySQL服务"
    
    if systemctl is-active --quiet "$MYSQL_SERVICE"; then
        systemctl restart "$MYSQL_SERVICE"
    else
        systemctl start "$MYSQL_SERVICE"
    fi
    
    sleep 10  # 等待服务启动
    
    if systemctl is-active --quiet "$MYSQL_SERVICE"; then
        log "MySQL服务重启成功"
        return 0
    else
        log "MySQL服务重启失败"
        return 1
    fi
}

# 调整MySQL连接超时参数
adjust_timeout_settings() {
    log "调整MySQL连接超时参数"
    
    # 仅当能连接到MySQL时调整参数
    if mysql -h"$MYSQL_HOST" -P"$MYSQL_PORT" -u"$MYSQL_USER" -p"$MYSQL_PASSWORD" -e "SELECT 1" >/dev/null 2>&1; then
        # 临时调整会话参数
        mysql -h"$MYSQL_HOST" -P"$MYSQL_PORT" -u"$MYSQL_USER" -p"$MYSQL_PASSWORD" -e "SET GLOBAL $CONN_TIMEOUT_PARAM = $CONN_TIMEOUT_VALUE;"
        
        # 查找my.cnf配置文件
        local cnf_files=("/etc/my.cnf" "/etc/mysql/my.cnf" "/usr/local/mysql/etc/my.cnf")
        for file in "${cnf_files[@]}"; do
            if [ -f "$file" ]; then
                # 永久调整配置文件
                if ! grep -qE "^$CONN_TIMEOUT_PARAM" "$file"; then
                    echo "$CONN_TIMEOUT_PARAM = $CONN_TIMEOUT_VALUE" >> "$file"
                    log "在$file中添加$CONN_TIMEOUT_PARAM配置"
                else
                    sed -i "s/^$CONN_TIMEOUT_PARAM.*/$CONN_TIMEOUT_PARAM = $CONN_TIMEOUT_VALUE/" "$file"
                    log "在$file中更新$CONN_TIMEOUT_PARAM配置"
                fi
                break
            fi
        done
        return 0
    else
        log "无法连接到MySQL，无法调整超时参数"
        return 1
    fi
}

# 主逻辑
main() {
    log "开始MySQL连接超时检查与处理"
    
    # 初始连接测试
    if test_mysql_connection; then
        log "MySQL连接正常，无需处理"
        exit 0
    fi
    
    log "检测到MySQL连接问题，启动处理流程"
    
    # 步骤1: 检查网络基础连通性
    if ! check_network_connectivity; then
        log "网络基础连通性有问题，尝试修复"
        if restart_network_service; then
            # 修复后再次测试连接
            if test_mysql_connection; then
                log "网络重启后MySQL连接恢复"
                exit 0
            fi
        else
            log "网络修复失败，继续其他检查"
        fi
    fi
    
    # 步骤2: 检查端口可达性
    if ! check_port_accessibility; then
        log "端口不可达，检查防火墙规则"
        if fix_firewall_rules; then
            sleep 5
            if test_mysql_connection; then
                log "防火墙规则修复后连接恢复"
                exit 0
            fi
        fi
    fi
    
    # 步骤3: 检查MySQL服务状态并重启
    if ! systemctl is-active --quiet "$MYSQL_SERVICE"; then
        log "MySQL服务未运行，尝试启动"
    else
        log "MySQL服务运行中但无法连接，尝试重启"
    fi
    
    if restart_mysql_service; then
        if test_mysql_connection; then
            log "MySQL服务重启后连接恢复"
            # 调整超时参数作为优化
            adjust_timeout_settings
            exit 0
        fi
    fi
    
    # 步骤4: 调整连接超时参数（如果可能）
    adjust_timeout_settings
    
    # 最终检查
    if test_mysql_connection; then
        log "MySQL连接已恢复"
        exit 0
    else
        log "所有修复措施均已尝试，但连接仍超时，需要人工干预"
        exit 1
    fi
}

# 执行主逻辑
main
    